﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Framework.Helper.Helpers.CurrentLimitingHelpers
{
    /// <summary>
    /// 限流控制器
    /// 实现令牌桶算法，控制指定时间窗口内的最大请求数
    /// </summary>
    /// <remarks>
    /// 功能特性：
    /// <para>
    /// 同步顺序执行，若上一周期内任务未执行完成，则会阻塞后续任务，等待上一周期任务执行完成后再执行
    /// </para>
    /// <para>1. 严格的FIFO队列</para>
    /// <para>2. 可配置的请求速率限制</para>
    /// <para>3. 线程安全的操作</para>
    /// <para>4. 自动令牌补充机制</para>
    /// </remarks>
    internal sealed class CurrentLimitingSync : IDisposable
    {
        private readonly string GroupName = Guid.NewGuid().ToString();

        /// <summary>
        /// 请求队列（线程安全阻塞集合）
        /// 保证请求按照先进先出(FIFO)顺序处理
        /// </summary>
        private readonly BlockingCollection<CurrentLimitingModel> _queue = new BlockingCollection<CurrentLimitingModel>();

        /// <summary>
        /// 令牌桶信号量
        /// 控制并发请求数量，初始和最大数量相同
        /// </summary>
        private readonly SemaphoreSlim _tokenBucket;

        /// <summary>
        /// 令牌补充定时器
        /// 定期重置可用令牌数（默认每秒重置）
        /// </summary>
        private readonly Timer _refillTimer;

        /// <summary>
        /// 单位时间最大请求数量
        /// </summary>
        private readonly int _maxRequests;

        /// <summary>
        /// 资源释放标记
        /// </summary>
        private bool _disposed;

        /// <summary>
        /// 限流控制器
        /// </summary>
        /// <param name="maxRequests">每秒最大请求数</param>
        /// <param name="refillIntervalMs">令牌补充间隔（毫秒）</param>
        /// <exception cref="ArgumentException">当参数不合法时抛出</exception>
        public CurrentLimitingSync(int maxRequests, int refillIntervalMs)
        {
            _maxRequests = maxRequests;
            _tokenBucket = new SemaphoreSlim(maxRequests, maxRequests);

            // 启动后台请求处理线程
            ThreadPool.QueueUserWorkItem(ProcessRequests);
            Console.WriteLine($"{DateTime.Now}->当前排队等候处理的工作项数量：{ThreadPool.PendingWorkItemCount}");
            // 初始化令牌补充定时器
            _refillTimer = new Timer(RefillTokens, null, refillIntervalMs, refillIntervalMs);
        }
        /// <summary>
        /// 限流控制器
        /// </summary>
        /// <param name="waitingTime">两次请求中最小时间间隔，单位：（毫秒）</param>
        /// <exception cref="ArgumentException">当参数不合法时抛出</exception>
        public CurrentLimitingSync(int waitingTime = 1, string groupName = null)
        {
            if (waitingTime > 0)
            {
                if (!string.IsNullOrWhiteSpace(groupName))
                {
                    GroupName = groupName;
                }
                // 计算每秒最大请求数
                _maxRequests = 1000 / waitingTime;
                _tokenBucket = new SemaphoreSlim(_maxRequests, _maxRequests);

                // 启动后台请求处理线程
                ThreadPool.QueueUserWorkItem(ProcessRequests);
                //Console.WriteLine($"{DateTime.Now}->当前排队等候处理的工作项数量：{ThreadPool.PendingWorkItemCount}");

                // 初始化令牌补充定时器
                _refillTimer = new Timer(RefillTokens, null, waitingTime, waitingTime);
            }
        }

        /// <summary>
        /// 将请求加入限流队列
        /// </summary>
        /// <param name="action">需要执行的请求操作</param>
        /// <returns>请求执行结果</returns>
        /// <exception cref="ObjectDisposedException">当限流器已释放时抛出</exception>
        public string EnqueueRequest(Func<string> action)
        {
            if (_disposed)
                throw new ObjectDisposedException(nameof(CurrentLimitingSync));

            var tcs = new TaskCompletionSource<string>();
            _queue.Add(new CurrentLimitingModel { Action = action, ResultTcs = tcs });
            return tcs.Task.GetAwaiter().GetResult();
        }

        /// <summary>
        /// 令牌补充方法
        /// </summary>
        private void RefillTokens(object state)
        {
            try
            {
                // 获取空闲线程数
                var currentCount = _tokenBucket.CurrentCount;
                if (currentCount < _maxRequests)
                {
                    Console.WriteLine($"{DateTime.Now}-{GroupName}->当前待补充的令牌数量：{_maxRequests - currentCount}，空闲线程数：{_tokenBucket.CurrentCount}，待处理任务数量：{_queue.Count}");
                    _tokenBucket.Release(_maxRequests - currentCount);
                }
            }
            catch (ObjectDisposedException)
            {
                // 忽略已释放的异常
            }
        }

        /// <summary>
        /// 请求处理线程方法
        /// </summary>
        private void ProcessRequests(object state)
        {
            while (!_disposed)
            {
                try
                {
                    // 阻塞直到获取待处理请求
                    var request = _queue.Take();

                    // 等待可用令牌（阻塞直到获取）
                    _tokenBucket.Wait();

                    try
                    {
                        // 执行实际请求操作
                        var result = request.Action();

                        // 设置成功结果
                        request.ResultTcs.SetResult(result);
                    }
                    catch (Exception ex)
                    {
                        // 捕获异常并传递回调用方
                        request.ResultTcs.SetException(ex);
                    }
                }
                catch (Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException)
                {
                    // 队列已释放时正常退出
                    break;
                }
                catch (Exception ex)
                {
                    throw new Exception($"请求处理线程发生意外错误", ex);
                }
            }
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            if (_disposed) return;

            _disposed = true;
            _refillTimer?.Dispose();
            _queue.CompleteAdding();
            _tokenBucket.Dispose();
        }
    }
}
